#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <signal.h>
#include <strings.h>

#include <list>
#include <string>

#include <boost/foreach.hpp>
#include <boost/typeof/typeof.hpp>

using namespace std;

void signal_handler(int sig);

const char *host_ip = "127.0.0.1";
const int   host_port = 6000;
bool b_is_running = true;

int main()
try {
    int server_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (server_socket == -1) {
        cerr << "create socket error" << endl;
        return 1;
    }

    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(host_ip);
    server_addr.sin_port = htons(host_port);

    if (bind(server_socket, (const struct sockaddr*)&server_addr, 
                sizeof(server_addr)) == -1) {
        cerr << "bind error" << endl;
        close(server_socket);
        return 1;
    }
    cout << "binding at " << host_port << endl;

    listen(server_socket, 10);

    cout << "--Start--" << endl;

    signal(SIGKILL, signal_handler);

    int epoll_fds = epoll_create(10);

    struct epoll_event ev;
    bzero(&ev, sizeof(ev));

    ev.events = EPOLLIN | EPOLLHUP;
    ev.data.fd = server_socket;
    epoll_ctl(epoll_fds, EPOLL_CTL_ADD, server_socket, &ev);

    const int buffer_size = 1024;
    char buffer[buffer_size] = {0};

    while (b_is_running) {
        struct epoll_event evs[10];
        int nfds = epoll_wait(epoll_fds, evs, 10, 0);
        if (nfds > 0) {
            for (int i = 0; i < nfds; ++i) {
                if (evs[i].data.fd == server_socket) {
                    struct sockaddr client_addr;
                    socklen_t client_addr_len;

                    int client_socket = accept(server_socket, &client_addr, &client_addr_len);
                    if (client_socket == -1) {
                        cerr << "accept error." << endl;
                    } else {
                        cout << "accept connect: " << client_socket << endl;
                        struct epoll_event ev;
                        bzero(&ev, sizeof(ev));
                        ev.events = EPOLLIN;
                        ev.data.fd = client_socket;

                        epoll_ctl(epoll_fds, EPOLL_CTL_ADD, client_socket, &ev);

                        string echo("Login Success!\n");
                        write(client_socket, echo.c_str(), echo.length());
                    }
                } else {
                    int this_fds = evs[i].data.fd;
                    if (evs[i].events & EPOLLIN) {
                        bzero(buffer, buffer_size);
                        int rsize = read(this_fds, buffer, buffer_size);
                        string echo("ECHO:");
                        echo += buffer;
                        write(this_fds, echo.c_str(), echo.length());

                    } else if (evs[i].events & EPOLLHUP) {
                        cout << "client hup: " << this_fds << endl;
                        epoll_ctl(epoll_fds, EPOLL_CTL_DEL, this_fds, NULL);
                        close(this_fds);
                    }
                }
            }
        }
    }

    close(server_socket);
    cout << "--End--" << endl;
    return 0;
} catch (...) {
    cerr << "Exception ocurr." << endl;
}

void signal_handler(int sig)
{
    b_is_running = false;    
    cout << "Ctrl+C" << endl;
}
